<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="generator" content="VuePress 2.0.0-beta.53" />
    <meta name="theme" content="VuePress Theme Hope" />
    <link rel="icon" href="/doc/images/favicon.ico"><title>第 4 章 函数 | AT 的文档库</title><meta name="description" content="At 的文档库，好记性不如烂笔头！">
    <style>
      :root {
        --bg-color: #fff;
      }

      html[data-theme="dark"] {
        --bg-color: #1d2025;
      }

      html,
      body {
        background: var(--bg-color);
      }
    </style>
    <script>
      const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
      const systemDarkMode =
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches;

      if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
        document.querySelector("html").setAttribute("data-theme", "dark");
      }
    </script>
    <link rel="preload" href="/doc/assets/style.c3a4c10b.css" as="style" /><link rel="stylesheet" href="/doc/assets/style.c3a4c10b.css" />
    <link rel="modulepreload" href="/doc/assets/app.41440b49.js"><link rel="modulepreload" href="/doc/assets/第 4 章 函数.html.9115df00.js"><link rel="modulepreload" href="/doc/assets/_plugin-vue_export-helper.cdc0426e.js"><link rel="modulepreload" href="/doc/assets/第 4 章 函数.html.701a2968.js"><link rel="prefetch" href="/doc/assets/index.html.0bcc895f.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.c8df2167.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.48e096c5.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.324e646c.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.fb0ad0e0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.d6c662f8.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.e976154a.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.e4f1b931.js" as="script" /><link rel="prefetch" href="/doc/assets/第 5 章 类和接口.html.8988d546.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.45b313e3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a9dd6465.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3374cf8.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.c326eaaf.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.88582229.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.e0f65550.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.87cccc41.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.c150b234.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3c2858b.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.9a53d157.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.101afd82.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8224e1cd.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.54b5ca2d.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.d839cc53.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.80e4d5cc.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.9a178a04.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.3535ab30.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.e926587b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.881e95b0.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.ba7314a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f9509ce0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章.html.726a0bb9.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章.html.38f55e7d.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.6b27764c.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.3849b10b.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.7d2793f1.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.c5de7dd9.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.86b08b0b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f4119260.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.caf7eb18.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.6dc23cf4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.868f2c80.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.9edf000e.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.76b27e4d.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.9ad8a3d4.js" as="script" /><link rel="prefetch" href="/doc/assets/第 5 章 类和接口.html.e392a0d1.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.1b5b864e.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.e5a75968.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.974654cf.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.ed3fa7a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.3b9ebcaa.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.b427e39a.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.76b3dd90.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.724416fc.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.8b55f9ef.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8ee968a7.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.06371751.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.6b672044.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.b200eaff.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.5135d2c4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.557b7bf5.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.7b535b09.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.c6b233af.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.450f0b1d.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.6483ed06.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.b4d859f3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f15cf3e9.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章.html.127b1e7c.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章.html.ea8e67e2.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.029df1e7.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.f102db2a.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.9bef3753.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.2cfd6b20.js" as="script" /><link rel="prefetch" href="/doc/assets/photoswipe.esm.09e03fed.js" as="script" />
  </head>
  <body>
    <div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="skip-link sr-only">Skip to content</a><!--]--><div class="theme-container has-toc"><!--[--><!--[--><header class="navbar"><div class="navbar-left"><button class="toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!--[--><!----><!--]--><a href="/doc/" class="brand"><!----><!----><span class="site-name">AT 的文档库</span></a><!--[--><!----><!--]--></div><div class="navbar-center"><!--[--><!----><!--]--><nav class="nav-links"><div class="nav-item hide-in-mobile"><a href="/doc/" class="nav-link" aria-label="首页"><!---->首页<!----></a></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="开发问题汇总"><span class="title"><!---->开发问题汇总</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/development-question/Vue" class="nav-link" aria-label="Vue"><!---->Vue<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="系统笔记"><span class="title"><!---->系统笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/sysnotes/Git/%E6%8E%8C%E6%8F%A1%20GIT.html" class="nav-link" aria-label="GIT"><!---->GIT<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Nvm/%E5%AE%89%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8.html" class="nav-link" aria-label="NVM"><!---->NVM<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Linux/%E5%9F%BA%E7%A1%80%E6%8E%8C%E6%8F%A1.html" class="nav-link" aria-label="Linux"><!---->Linux<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/JavaScript" class="nav-link" aria-label="JavaScript"><!---->JavaScript<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/CSS" class="nav-link" aria-label="CSS"><!---->CSS<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/HTML" class="nav-link" aria-label="HTML"><!---->HTML<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="阅读笔记"><span class="title"><!---->阅读笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/readbook/《Vue.js 设计与实现》/" class="nav-link" aria-label="《Vue.js 设计与实现》"><!---->《Vue.js 设计与实现》<!----></a></li><li class="dropdown-item"><a href="/doc/readbook/《TypeScript 编程》/" class="nav-link" aria-label="《TypeScript 编程》"><!---->《TypeScript 编程》<!----></a></li></ul></button></div></div></nav><!--[--><!----><!--]--></div><div class="navbar-right"><!--[--><!----><!--]--><div class="nav-item hide-in-mobile"><button id="appearance-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="light icon" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><form class="search-box" role="search"><input type="search" autocomplete="off" spellcheck="false" value><!----></form><!--[--><!----><!--]--><button class="toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span class="button-container"><span class="button-top"></span><span class="button-middle"></span><span class="button-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow left"></span></div><aside class="sidebar"><!--[--><!----><!--]--><ul class="sidebar-links"><li><section class="sidebar-group"><button class="sidebar-heading clickable active"><!----><span class="title">《TypeScript 编程》</span><span class="arrow down"></span></button><ul class="sidebar-links"><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%201%20%E7%AB%A0%20%E5%AF%BC%E8%A8%80.html" class="nav-link sidebar-link sidebar-page" aria-label="第 1 章 导言"><!---->第 1 章 导言<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%202%20%E7%AB%A0%20TypeScript%20%E6%A6%82%E8%BF%B0.html" class="nav-link sidebar-link sidebar-page" aria-label="第 2 章 TypeScript 概述"><!---->第 2 章 TypeScript 概述<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%203%20%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E5%85%A8%E8%A7%A3.html" class="nav-link sidebar-link sidebar-page" aria-label="第 3 章 类型全解"><!---->第 3 章 类型全解<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html" class="router-link-active router-link-exact-active nav-link active sidebar-link sidebar-page active" aria-label="第 4 章 函数"><!---->第 4 章 函数<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-声明和调用函数" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1 声明和调用函数"><!---->4.1 声明和调用函数<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-1-可选和默认参数" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.1 可选和默认参数"><!---->4.1.1 可选和默认参数<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-2-剩余参数" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.2 剩余参数"><!---->4.1.2 剩余参数<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-3-call、apply-和-bind" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.3 call、apply 和 bind"><!---->4.1.3 call、apply 和 bind<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-4-注解-this-的类型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.4 注解 this 的类型"><!---->4.1.4 注解 this 的类型<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-5-生成器函数" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.5 生成器函数"><!---->4.1.5 生成器函数<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-6-迭代器" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.6 迭代器"><!---->4.1.6 迭代器<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-7-调用签名" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.7 调用签名"><!---->4.1.7 调用签名<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-8-上下文类型推导" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.8 上下文类型推导"><!---->4.1.8 上下文类型推导<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-9-函数类型重载" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.1.9 函数类型重载"><!---->4.1.9 函数类型重载<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-多态" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2 多态"><!---->4.2 多态<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-1-什么时候绑定泛型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2.1 什么时候绑定泛型"><!---->4.2.1 什么时候绑定泛型<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-2-可以在什么地方声明" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2.2 可以在什么地方声明"><!---->4.2.2 可以在什么地方声明<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-3-泛型推导" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2.3 泛型推导"><!---->4.2.3 泛型推导<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-4-泛型别名" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2.4 泛型别名"><!---->4.2.4 泛型别名<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-5-受限的多态" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2.5 受限的多态"><!---->4.2.5 受限的多态<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-6-泛型默认类型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.2.6 泛型默认类型"><!---->4.2.6 泛型默认类型<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-3-类型驱动开发" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="4.3 类型驱动开发"><!---->4.3 类型驱动开发<!----></a><ul class="sidebar-sub-headers"></ul></li></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html" class="nav-link sidebar-link sidebar-page" aria-label="第 5 章 类和接口"><!---->第 5 章 类和接口<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%206%20%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E8%BF%9B%E9%98%B6.html" class="nav-link sidebar-link sidebar-page" aria-label="第 6 章 类型进阶"><!---->第 6 章 类型进阶<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section></li><li><section class="sidebar-group"><button class="sidebar-heading clickable"><!----><span class="title">《Vue.js 设计与实现》</span><span class="arrow right"></span></button><!----></section></li></ul><!--[--><!----><!--]--></aside><!--[--><main class="page" id="main-content"><!--[--><!----><nav class="breadcrumb disable"></nav><div class="page-title"><h1><!---->第 4 章 函数</h1><div class="page-info"><!----><!----><!----><!----><!----><span class="reading-time-info" aria-label="阅读时间⌛" data-balloon-pos="down"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="timer icon"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>大约 22 分钟</span><meta property="timeRequired" content="PT22M"></span></div><hr></div><div class="toc-place-holder"><aside id="toc"><div class="toc-header">此页内容</div><div class="toc-wrapper"><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-声明和调用函数" class="router-link-active router-link-exact-active toc-link level2">4.1 声明和调用函数</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-1-可选和默认参数" class="router-link-active router-link-exact-active toc-link level3">4.1.1 可选和默认参数</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-2-剩余参数" class="router-link-active router-link-exact-active toc-link level3">4.1.2 剩余参数</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-3-call、apply-和-bind" class="router-link-active router-link-exact-active toc-link level3">4.1.3 call、apply 和 bind</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-4-注解-this-的类型" class="router-link-active router-link-exact-active toc-link level3">4.1.4 注解 this 的类型</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-5-生成器函数" class="router-link-active router-link-exact-active toc-link level3">4.1.5 生成器函数</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-6-迭代器" class="router-link-active router-link-exact-active toc-link level3">4.1.6 迭代器</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-7-调用签名" class="router-link-active router-link-exact-active toc-link level3">4.1.7 调用签名</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-8-上下文类型推导" class="router-link-active router-link-exact-active toc-link level3">4.1.8 上下文类型推导</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-1-9-函数类型重载" class="router-link-active router-link-exact-active toc-link level3">4.1.9 函数类型重载</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-多态" class="router-link-active router-link-exact-active toc-link level2">4.2 多态</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-1-什么时候绑定泛型" class="router-link-active router-link-exact-active toc-link level3">4.2.1 什么时候绑定泛型</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-2-可以在什么地方声明" class="router-link-active router-link-exact-active toc-link level3">4.2.2 可以在什么地方声明</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-3-泛型推导" class="router-link-active router-link-exact-active toc-link level3">4.2.3 泛型推导</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-4-泛型别名" class="router-link-active router-link-exact-active toc-link level3">4.2.4 泛型别名</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-5-受限的多态" class="router-link-active router-link-exact-active toc-link level3">4.2.5 受限的多态</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-2-6-泛型默认类型" class="router-link-active router-link-exact-active toc-link level3">4.2.6 泛型默认类型</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html#_4-3-类型驱动开发" class="router-link-active router-link-exact-active toc-link level2">4.3 类型驱动开发</a></li><!----><!--]--></ul></div></aside></div><!----><div class="theme-hope-content"><h1 id="第-4-章-函数" tabindex="-1"><a class="header-anchor" href="#第-4-章-函数" aria-hidden="true">#</a> 第 4 章 函数</h1><h2 id="_4-1-声明和调用函数" tabindex="-1"><a class="header-anchor" href="#_4-1-声明和调用函数" aria-hidden="true">#</a> 4.1 声明和调用函数</h2><p>在 JavaScript 中函数是一等公民。意味着，可以想对象一样使用函数：赋值给变量；也可以作为参数传给其它函数；可以作为函数的返回值； 可以赋值给对象和原型；可以赋予属性；可以读取属性等。</p><p>建议显示注解函数的参数。TypeScript 能推导出函数体中的类型，但是多数情况下无法推导出参数的类型，只在少数特殊情况下能根据上下文推导出参数的类型。 返回类型能推导出来也可以显示注解。</p><p>TypeScript 和 JavaScript 一样支持五种声明函数的方式：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 具名函数</span>
<span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>name<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>

<span class="token comment">// 函数表达式</span>
<span class="token keyword">const</span> <span class="token function-variable function">foo</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span>name<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>

<span class="token comment">// 箭头函数表达式</span>
<span class="token keyword">const</span> <span class="token function-variable function">foo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>name<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span> 
<span class="token punctuation">}</span>

<span class="token comment">// 箭头函数表达式简写</span>
<span class="token keyword">const</span> <span class="token function-variable function">foo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>name<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">=&gt;</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Hello </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>name<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span> 

<span class="token comment">// 函数构造方法</span>
<span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token builtin">Function</span></span><span class="token punctuation">(</span><span class="token string">&#39;name&#39;</span><span class="token punctuation">,</span><span class="token string">&#39;return &quot;Hello&quot; + name&#39;</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>除去函数构造方法外，其他几种方式都可以放心使用，能确保类型安全。在 JavaScript 中也基本不会使用函数构造方法声明函数。</p><p>函数注解规则：参数类型、返回类型不必须注解</p><div class="custom-container tip"><p class="custom-container-title">提示</p><ul><li>形参（parameter）是声明函数时指定的运行函数所需要的数据</li><li>实参（argument） 是调用函数时传给函数的实际数据</li></ul></div><p>在 TypeScript 中调用函数时，无需提供任何额外的类型信息，直接传入实参即可，TypeScript 将检查实参是否与参数形参类型一致。</p><h3 id="_4-1-1-可选和默认参数" tabindex="-1"><a class="header-anchor" href="#_4-1-1-可选和默认参数" aria-hidden="true">#</a> 4.1.1 可选和默认参数</h3><p>声明函数的参数必要的参数放在前，随后才是可选的参数：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">log</span><span class="token punctuation">(</span>message<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">,</span>userId<span class="token operator">?</span><span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> time <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLocaleTimeString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>time<span class="token punctuation">,</span>message<span class="token punctuation">,</span>userId<span class="token operator">||</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>也可以为可选参数提供默认值。带默认值的参数不要求放在末尾，而可选参数必须放在末尾。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">log</span><span class="token punctuation">(</span>message<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">,</span>userId <span class="token operator">=</span> <span class="token string">&#39;Not signed in&#39;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> time <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLocaleTimeString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>time<span class="token punctuation">,</span>message<span class="token punctuation">,</span>userId<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>带有默认值的参数可以不显示注解类型，TypeScript 能够根据默认值推导出参数的类型。</p><p>当然也可以显示注解默认参数的类型：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Context</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
    appId<span class="token operator">?</span><span class="token operator">:</span><span class="token builtin">string</span>
    userId<span class="token operator">?</span><span class="token operator">:</span><span class="token builtin">string</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">log</span><span class="token punctuation">(</span>message<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">,</span>context<span class="token operator">:</span>Context <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> time <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLocaleTimeString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>time<span class="token punctuation">,</span>message<span class="token punctuation">,</span>Context<span class="token punctuation">.</span>userId<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>较之可选参数，更加常用默认参数。</p><h3 id="_4-1-2-剩余参数" tabindex="-1"><a class="header-anchor" href="#_4-1-2-剩余参数" aria-hidden="true">#</a> 4.1.2 剩余参数</h3><p>平时开发有时需要函数的参数是可变的，即参数数量不固定。之前可以通过 JavaScript 函数的 arguments 对象实现。</p><p>arguments 是 JavaScript 在运行时自动在函数内定义该对象，并把传给函数的参数列表赋予该对象。arguments 是一个类数组对象。在调用数组方法时需要将该对象转为数组。</p><p>但是在 TypeScript 中使用 arguments 并不是很安全。经 TypeSript 推导出的值是 any 类型。</p><p><img src="/doc/assets/arguments.5f92c2d0.jpg" alt="arguments 不安全"></p><p>由于声明 sumVariadic 函数时没有指定参数，因此在 TypeScript 看来，该函数不接受任何参数，所以在使用时会得到 TypeError。</p><p>如果确保可变参数函数的类型安全，就要使用剩余参数（rest parameter）。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">sumVariadicSafe</span><span class="token punctuation">(</span><span class="token operator">...</span>numbers<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> numbers<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span>total<span class="token punctuation">,</span> n<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> total <span class="token operator">+</span> n<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">sumVariadicSafe</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一个函数最多只能有一个剩余参数，而且必须位于参数列表的最后。</p><h3 id="_4-1-3-call、apply-和-bind" tabindex="-1"><a class="header-anchor" href="#_4-1-3-call、apply-和-bind" aria-hidden="true">#</a> 4.1.3 call、apply 和 bind</h3><p>调用函数除了使用&quot;()&quot;外，JavaScript 还支持其他方式：call、apply 和 bind。这三种方式可以改变函数内部的 this 指向。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span>a<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> b<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">number</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>apply 为函数内部的 this 绑定一个值，然后展开第二个参数，作为参数传给要调用的函数。call 的用法类似，不过是按照顺序应用参数。</p><p>bind 也为函数的 this 和参数绑定值。bind 并不是直接调用函数，而是返回一个新的函数。</p><div class="custom-container tip"><p class="custom-container-title">TSC 标志：strictBindCallApply</p><p>为了安全使用，call、apply 和 bind，要在 tsconfig.json 中启用 strictBindCallApply 选项。(启用 strict 选项默认开启该选项)</p></div><h3 id="_4-1-4-注解-this-的类型" tabindex="-1"><a class="header-anchor" href="#_4-1-4-注解-this-的类型" aria-hidden="true">#</a> 4.1.4 注解 this 的类型</h3><p>JavaScript 的每一个函数都有 this。以不同的方式调用 this 的值也不通，这极其容易导致代码脆弱、难以理解。</p><div class="custom-container tip"><p class="custom-container-title">提示</p><p>很多团队禁止在类方法以外使用 this,可以启用 TSLint 的 no-invalid-this 规则</p></div><p>this 之所以脆弱，与它的赋值方式有关。一般来说，this 的值为调用方法时位于点号左侧的对象。</p><p>举个🌰：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">let</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
    <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">this</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

o<span class="token punctuation">.</span><span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中 <code>o.a()</code>，函数 a 中的 this 就是 对象 o。</p><p>如果在调用之前重新赋值后 this 将发生改变。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">let</span> a <span class="token operator">=</span> o<span class="token punctuation">.</span>a
<span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div></div></div><p>此时 this 为全局对象或者 undefined。</p><p>在 TypeScript 函数中使用 this，要在函数的<strong>第一个</strong>参数中声明 this 的类型。this 不是常规参数，而是保留字，是函数签名的一部分。</p><div class="custom-container tip"><p class="custom-container-title">TSC 标志: noImplicitThis</p><p>如果强制险是注解函数中的 this 的类型，在 tsconfig.json 中启用 noImplicitThis 设置项(启用 strict 选项默认开启该选项)。</p><p>注意，noImplicitThis 不强制要求为类型或对象的函数注解 this。</p></div><h3 id="_4-1-5-生成器函数" tabindex="-1"><a class="header-anchor" href="#_4-1-5-生成器函数" aria-hidden="true">#</a> 4.1.5 生成器函数</h3><p>生成器函数（简称生成器）是生成一系列值得便利方式。生成器的使用可以精确控制生成什么值。声称其实<strong>惰性的</strong>，只在使用方要求时才计算下一个值。</p><p>举个🌰：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 生成无穷列表</span>
<span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">createFibonacciGenerator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  <span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">yield</span> a<span class="token punctuation">;</span>
    <span class="token punctuation">[</span>a<span class="token punctuation">,</span> b<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span>b<span class="token punctuation">,</span> a <span class="token operator">+</span> b<span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> foo <span class="token operator">=</span> <span class="token function">createFibonacciGenerator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>foo<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { value: 0, done: false }</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>foo<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { value: 1, done: false }</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>foo<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { value: 1, done: false }</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>foo<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// { value: 2, done: false }</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol><li>函数名前的 * 表示该函数为一个生成器函数。调用生成器返回一个可迭代的迭代器。</li><li>生成器使用 yield 关键字产出值。调用方让生成器提供下一个值时（如调用 next）,yiled 把结果返回给调用方，然后停止执行。</li><li>上述代码中 <code>[a, b] = [b, a + b]</code> 计算下一个斐波那契数（“兔子数列”）<code>F[n]=F[n-1]+F[n-2](n&gt;=2,F[0]=0,F[1]=1)</code>。把 b 赋值给 a、把 a + b 赋值给 b。</li></ol><p>调用 createFibonacciGenerator 得到一个 IterableIterator。每次调用 next，迭代器计算下一个斐波那契数值。</p><h3 id="_4-1-6-迭代器" tabindex="-1"><a class="header-anchor" href="#_4-1-6-迭代器" aria-hidden="true">#</a> 4.1.6 迭代器</h3><p>迭代器是生成器的相对面：生成器是生成一系列值的方式，而迭代器时使用这些值的方式。</p><blockquote><p>可迭代对象：有 Symbol.iterator 属性的对象，而且该属性值的值为一个函数，返回一个迭代器。</p><p>迭代器：定义有 next 方法的对象，该方法返回一个具有 value 和 done 属性的对象。</p></blockquote><p>创建生成器，得到的值及时可迭代对象，也是迭代器，称之为可迭代的迭代器，因为该值既有 Symbol.iterator 属性也有 next 方法。</p><p>可以手动定义迭代器或可迭代对象，只需分别实现 Symbol.iterator 属性和 next 方法的对象或类：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 自定义迭代器</span>
<span class="token keyword">let</span> numbers <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token operator">*</span><span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> n <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> n <span class="token operator">&lt;</span> <span class="token number">10</span><span class="token punctuation">;</span> n<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">yield</span> n<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>numbers 是一个迭代器，调用生成器函数 <code>numbers[Symbol.iterator]()</code> 返回一个可迭代对象。</p><p>除可自己定义迭代器外，还可以使用 JavaScript 内置的常用集合类型(Array、Map、Set、String 等)迭代器：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">let</span> numbers <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token operator">*</span><span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> n <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> n <span class="token operator">&lt;=</span> <span class="token number">10</span><span class="token punctuation">;</span> n<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">yield</span> n<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> n <span class="token keyword">of</span> numbers<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> allNums <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>numbers<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>allNums<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> <span class="token punctuation">[</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> <span class="token operator">...</span>rest<span class="token punctuation">]</span> <span class="token operator">=</span> numbers<span class="token punctuation">;</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> rest<span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="custom-container tip"><p class="custom-container-title">提示</p><p>Object 和 Number 不是迭代器</p></div><div class="custom-container tip"><p class="custom-container-title">TSC 标志：downlevelIteration</p><p>如果把 TypeScript 编译成早于 ES6 的版本，可在 tsconfig.json 中设置 downlevelIteration，启用自定义迭代器。</p><p>如果特别在意打包体积，应该禁用 downlevelIteration，因为在陈旧的环境中，生成自定义迭代器的代码非常多。</p></div><h3 id="_4-1-7-调用签名" tabindex="-1"><a class="header-anchor" href="#_4-1-7-调用签名" aria-hidden="true">#</a> 4.1.7 调用签名</h3><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">sum</span><span class="token punctuation">(</span>a<span class="token operator">:</span><span class="token builtin">number</span><span class="token punctuation">,</span>b<span class="token operator">:</span><span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> a <span class="token operator">+</span> b
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中：sum 函数的类型是 <code>Function</code>,但是 <code>Function</code> 可以表示所有函数，和 <code>object</code> 一样表示所有对象。具体表现的类型不是很明确。 在 TypeScript 中，根据该函数的参数和返回值可以使用 <code>(a:number,b:number) =&gt; number</code> 表示函数类型，也称之为调用签名（或叫类型签名）。和箭头函数的句法 相似。如果把函数作为参数传给另一个函数，或者作为其他函数的返回值，就要使用类型签名注解。</p><p>函数的调用签名只包含类型层面的代码，即只有类型，没有值。因此，函数的调用签名可以表示参数的类型、this 的类型、返回值的类型、剩余参数的类型和可选参数的类型，但是无法表示默认值。 调用签名没有函数的定义体，无法推导出返回来兴，所以必须显示注解。</p><div class="custom-container warning"><p class="custom-container-title">类型层面和值层面代码</p><p>讨论静态语言时，尝试用 &quot;类型层面&quot; 和 &quot;值层面&quot; 两个术语。</p><p>&quot;类型层面代码&quot; 指只有类型和类型运算符的代码。本书当中，如果是有效的 JavaScript 代码就是 &quot;值层面&quot; 代码；如果是有效的 TypeScript 代码但不是有效的 JavaScript 代码那就是 &quot;类型层面&quot; 代码。</p><p>这个判断标准不适用枚举和命名空间，枚举既生成类型也生成值。而命名空间只存在于值层面。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">area</span><span class="token punctuation">(</span>radius<span class="token operator">:</span><span class="token operator">**</span><span class="token builtin">number</span><span class="token operator">**</span><span class="token punctuation">)</span><span class="token operator">:</span><span class="token operator">**</span><span class="token builtin">number</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token operator">**</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span><span class="token punctuation">(</span>radius<span class="token operator">&lt;</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">null</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> Math<span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator">*</span> <span class="token punctuation">(</span>radius<span class="token operator">**</span><span class="token number">2</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> r<span class="token operator">:</span><span class="token operator">**</span><span class="token builtin">number</span><span class="token operator">**</span> <span class="token operator">=</span> <span class="token number">3</span>
<span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token function">area</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span>
<span class="token keyword">if</span><span class="token punctuation">(</span>a <span class="token operator">!==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中使用 **** 包裹的代码就是 &quot;类型层面&quot;，其余是 &quot;值层面&quot; 代码。</p></div><p>使用单独的调用签名表示函数类型：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Greet</span> <span class="token operator">=</span> <span class="token punctuation">(</span>name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">greet</span><span class="token punctuation">(</span>name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">type</span> <span class="token class-name">Log</span> <span class="token operator">=</span> <span class="token punctuation">(</span>message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> userId<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">log</span><span class="token punctuation">(</span>message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> userId<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">type</span> <span class="token class-name">SumVariadicSafe</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token operator">...</span>nums<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">SumVariadicSafe</span><span class="token punctuation">(</span><span class="token operator">...</span>nums<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>结合使用：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Log</span> <span class="token operator">=</span> <span class="token punctuation">(</span>message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> userId<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> log<span class="token operator">:</span> Log <span class="token operator">=</span> <span class="token punctuation">(</span>message<span class="token punctuation">,</span> userId <span class="token operator">=</span> <span class="token string">&quot;Not signed in&quot;</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> time <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toISOString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>time<span class="token punctuation">,</span> userId<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>声明一个函数表达式 log，显示注解其类型为 Log</li><li>参数类型不需要再次注解，因为在定义 Log 类型是已经注解。TypeScript 会从 Log 中推导出</li><li>userId 设置默认值，userId 类型可以从 Log 中获取，但是默认值却不清楚，因为 Log 是类型，不包含值</li><li>无需再次注解返回类型，Log 中已经显示注解</li></ul><h3 id="_4-1-8-上下文类型推导" tabindex="-1"><a class="header-anchor" href="#_4-1-8-上下文类型推导" aria-hidden="true">#</a> 4.1.8 上下文类型推导</h3><p>上述 log 方法，无需使用显示注解函数参数类型。因为已经把 log 的类型声明为 Log，所以 TypeScript 能从上下文中推导出 message 的类型为 string。 这是 TypeScript 类型推导的一个强大特性，称之为上下文类型推导（contextual typing）</p><p>回调函数使用调用签名：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">time</span><span class="token punctuation">(</span><span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token punctuation">(</span>index<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">,</span> n<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> n<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">callback</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token function">time</span><span class="token punctuation">(</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在使用 time 方法时如果是行内传入函数的话，无需再显示注解参数类型，TypeScript 能从上下文中推导出 n 是数字。</p><p>如果 callback 函数是单独声明然后传入的话，TypeScript 则无法推导出类型。</p><h3 id="_4-1-9-函数类型重载" tabindex="-1"><a class="header-anchor" href="#_4-1-9-函数类型重载" aria-hidden="true">#</a> 4.1.9 函数类型重载</h3><p>上述调用签名都是简写形式：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 简写</span>
<span class="token keyword">type</span> <span class="token class-name">Log</span> <span class="token operator">=</span> <span class="token punctuation">(</span>message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> userId<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>

<span class="token comment">// 完整</span>
<span class="token keyword">type</span> <span class="token class-name">Log</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> userId<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>简单情况下使用简写，复杂的函数使用完整签名。</p><p>首先是重载函数类型。</p><blockquote><p>重载函数：有多个调用签名的函数。</p></blockquote><p>在多数编程语言中，声明函数时，一旦指定了特定的参数和返回类型，就只能使用相应的参数调用函数，而且返回值的类型始终如一。 JavaScript 并非如此，因为 JavaScript 是动态语言，势必需要以多种方式调用一个函数，而且又是输出的类型取决于输入的参数类型。</p><p>TypeScript 支持动态重载函数声明，而且函数的输出类型取决于输入类型，因为 TypeScript 是静态类型系统。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Reserve</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>from<span class="token operator">:</span> Date<span class="token punctuation">,</span> to<span class="token operator">:</span> Date<span class="token punctuation">,</span> destination<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span>from<span class="token operator">:</span> Date<span class="token punctuation">,</span> destination<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> reserve<span class="token operator">:</span> <span class="token function-variable function">Reserve</span> <span class="token operator">=</span> <span class="token punctuation">(</span>from<span class="token punctuation">,</span> to<span class="token punctuation">,</span> destination<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码由于 TypeScript 的调用签名重载机制造成类型错误提示。如果函数声明多个重载签名，在调用方看来，该函数的类型是各签名的并集。 但是再实现函数时，必须一次实现整个类型组合。要自己设法声明组合后的调用签名，TypeScript 无法自动推导类型。</p><p>上述示例代码针对定义方法时调整：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Reserve</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>from<span class="token operator">:</span> Date<span class="token punctuation">,</span> to<span class="token operator">:</span> Date<span class="token punctuation">,</span> destination<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token punctuation">(</span>from<span class="token operator">:</span> Date<span class="token punctuation">,</span> destination<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> reserve<span class="token operator">:</span> <span class="token function-variable function">Reserve</span> <span class="token operator">=</span> <span class="token punctuation">(</span>
  from<span class="token operator">:</span> Date<span class="token punctuation">,</span>
  toOrDestination<span class="token operator">:</span> Date <span class="token operator">|</span> <span class="token builtin">string</span><span class="token punctuation">,</span>
  destination<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">string</span>
<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol><li>声明两个重载的函数签名</li><li>手动组合两个签名（计算两个签名的并集），实现声明的签名。注意，虽然在声明函数时，显示注解了各参数的类型，但是组合后的签名（即声明函数时的显示注解）对使用方是不可见的。</li></ol><div class="custom-container tip"><p class="custom-container-title">让重载的签名具体一些</p><p>一般来说，声明重载函数类型时，每个<strong>重载的签名（如：Reserve）</strong> 都必须可以赋值给 <strong>实现的签名（reserve）</strong>。但是，声明实现的签名时一般可能会更宽泛一些，保证所有重载的签名都可以赋值给实现的签名。</p><p>如下：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">let</span> reserve<span class="token operator">:</span> <span class="token function-variable function">Reserve</span> <span class="token operator">=</span> <span class="token punctuation">(</span>
  from<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span>
  toOrDestination<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">,</span>
  destination<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span>
<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token string">&quot;&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>重载时，应该尽量让<strong>实现的签名具体些</strong>，这样更容易实现函数，因此，在实力当中时使用 Date 好过 any，使用 Date | string 并集好过 any。</p><p>对特定的签名来说让类型的范围窄一些更易于实现函数。如果把参数类型注解为 any，在调用函数时想要传入 Date 类型参数，就得需要函数内部做类型判断，如果在定义函数时显示注解具体的参数类型。TypeScript 会做出校验，无需增加类型的代码判断。</p></div><p>浏览器 DOM API 有大量重载。如 createElement，参数为表示 HTML 标签字符串，返回值为对应的 HTML 元素。</p><p>TypeScript 内置了每个 HTML 元素类型，如：</p><ul><li>&lt;a&gt; 元素的 HTMLAnchorElement</li><li>&lt;canvas&gt; 元素的 HTMLCanvasElement</li><li>&lt;table&gt; 元素的 HTMLTableElement</li></ul><p>通过重载调用签名实现 createElement：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">CreateElement</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>tag<span class="token operator">:</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLAnchorElement<span class="token punctuation">;</span>
  <span class="token punctuation">(</span>tag<span class="token operator">:</span> <span class="token string">&quot;canvas&quot;</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLCanvasElement<span class="token punctuation">;</span>
  <span class="token punctuation">(</span>tag<span class="token operator">:</span> <span class="token string">&quot;table&quot;</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLTableElement<span class="token punctuation">;</span>
  <span class="token punctuation">(</span>tag<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLElement<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> createElement<span class="token operator">:</span> CreateElement <span class="token operator">=</span> <span class="token punctuation">(</span>tag<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> HTMLElement <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>重载参数的类型，与字符串字面量类型匹配</li><li>添加一个兜底情况：如果用户换入自定义的标签名，TypeScript 没有内置对应的类型声明，返回一般性的 HTMLElement。TypeScript 按照声名顺序解析重载，调用函数时如果传入没有对应重载定义的字符串，TypeScript 将回落到 HTMLElement。</li><li>注解实现参数时，要把参数在 createElement 重载签名中的所有类型组合在一起，得到 &#39;a&#39; | &#39;canvas&#39; | &#39;table&#39; | string。由于前三个字符字面量类型是 string 的子类型，所以可以把最终结果简化为 string。</li></ul><p>重载函数只有声明函数的形式为函数表达式才可以，如果是函数式声明则不可以使用。</p><p>完整的类型签名并不只限于用来重载调用函数的方式，还可以描述函数的属性。由于 JavaScript 的函数是可调用对象，因此可以为函数赋予属性：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token function">warnUser</span><span class="token punctuation">(</span>warning<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>warnUser<span class="token punctuation">.</span>wasCalled<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  warnUser<span class="token punctuation">.</span>wasCalled <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>warning<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
warnUser<span class="token punctuation">.</span>wasCalled <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>

<span class="token function">warnUser</span><span class="token punctuation">(</span><span class="token string">&quot;111111&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 111111</span>
<span class="token function">warnUser</span><span class="token punctuation">(</span><span class="token string">&quot;222222&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 直接 return</span>



<span class="token keyword">type</span> <span class="token class-name">WarnUser</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>warning<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
  wasCalled<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span> <span class="token comment">// 新版 ts 会报错</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> warnUser<span class="token operator">:</span> <span class="token function-variable function">WarnUser</span> <span class="token operator">=</span> <span class="token punctuation">(</span>warning<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>warnUser<span class="token punctuation">.</span>wasCalled<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  warnUser<span class="token punctuation">.</span>wasCalled <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>warning<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_4-2-多态" tabindex="-1"><a class="header-anchor" href="#_4-2-多态" aria-hidden="true">#</a> 4.2 多态</h2><p>使用具体类型的前提是明确知道需要什么类型，并且想确认传入的确实是那个类型。 有时事先并不知道需要什么类型，不想限制函数只能接受某个类型。</p><blockquote><p>泛型参数：在类型层面施加约束的占位类型，也称多态类型。</p></blockquote><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Filter</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>意思是， filter 函数使用一个泛型 T，T 的类型不清楚，TypeScript 中，调用 filter 函数时，会从传入的 array 中推导 T 的类型。 调用 filter 时，TypeScript 推导出 T 的具体类型后，将把 T 出现的每一处替换为推导出的类型。T 就像一个占位类型，类型检查器将根据上下文填充具体的类型。 T 把 Filter 的类型参数化，所以才称之为反省参数，简称<strong>泛型</strong>。</p><p><img src="/doc/assets/4.2.1.375fd670.jpg" alt="Filter"></p><p>图中代码所示，当传入的参数为数字组成的数组，TypeScript 推导出 T 的类型为 number。</p><p>泛型参数使用尖括号 &lt;&gt; 声明（type 关键字，声明泛型）。尖括号的位置限定泛型的作用域（只有少数几个地方可以使用尖括号）。TypeScript 将确保当前作用域中相同的泛型参数最终都绑定同一个具体类型。在一堆尖括号中 可以声明任意个以逗号分割的泛型参数。</p><div class="custom-container tip"><p class="custom-container-title">提示</p><p>T 就是一个类型名称，可以使用其他名称。按照惯例经常使用单个大写字母，从 T 开始，依次使用 U、V、W。</p><p>如果一行声明中的泛型较多，或者使用方式较为复杂，可以不遵守，转而使用具有描述性的名称。</p></div><p>泛型让函数的功能更具有一般性，比接受具体类型的函数更强大。泛型可以理解为一种约束。泛型 T 把 T 所在的位置的类型约束为 T 绑定的类型。</p><div class="custom-container tip"><p class="custom-container-title">提示</p><p>泛型可以在类型别名、类和接口中使用。只要可能就应该使用泛型，这样写出的代码更具一般性，可重复使用。</p></div><h3 id="_4-2-1-什么时候绑定泛型" tabindex="-1"><a class="header-anchor" href="#_4-2-1-什么时候绑定泛型" aria-hidden="true">#</a> 4.2.1 什么时候绑定泛型</h3><p>声明反省的位置不仅限定泛型的作用域，还决定 TypeScript 什么时候为泛型绑定具体的类型。</p><p>以上节代码为例，&lt;T&gt; 在调用签名中声明（位于签名的开始圆括号前面），TypeScript 将在调用 Filter 类型的函数时为 T 绑定具体类型。</p><p>如果把 T 的作用域限定在类型别名 Filter 中，TypeScript 则要求在使用 Filter 时显示绑定：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Filter<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> filter<span class="token operator">:</span> Filter<span class="token operator">&lt;</span><span class="token builtin">number</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">(</span>array<span class="token punctuation">,</span> f<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> array<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> item <span class="token operator">=</span> array<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">f</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      result<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一般来说：</p><ul><li>TypeScript 在使用泛型时为泛型绑定具体类型</li><li>对函数来说，在调用函数时</li><li>对类来说在实例化类时</li><li>对类型别名和接口来说，在使用别名和实现接口时</li></ul><h3 id="_4-2-2-可以在什么地方声明" tabindex="-1"><a class="header-anchor" href="#_4-2-2-可以在什么地方声明" aria-hidden="true">#</a> 4.2.2 可以在什么地方声明</h3><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 方式一</span>
<span class="token keyword">type</span> <span class="token class-name">Filter</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> filter<span class="token operator">:</span>Filter <span class="token operator">=</span> <span class="token operator">...</span>
<span class="token comment">// 方式二</span>
<span class="token keyword">type</span> <span class="token class-name">Filter<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> filter<span class="token operator">:</span>Filter<span class="token operator">&lt;</span><span class="token builtin">number</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token operator">...</span>
<span class="token comment">// 方式三</span>
<span class="token keyword">type</span> <span class="token class-name">Filter</span> <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> filter<span class="token operator">:</span>Filter <span class="token operator">=</span> <span class="token operator">...</span>
<span class="token comment">// 方式四</span>
<span class="token keyword">type</span> <span class="token class-name">Filter<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> filter<span class="token operator">:</span>Filter<span class="token operator">&lt;</span><span class="token builtin">number</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token operator">...</span>
<span class="token comment">// 方式五</span>
<span class="token keyword">function</span> <span class="token generic-function"><span class="token function">filter</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">boolean</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span>
    <span class="token operator">...</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol><li>方式一：一个完整的调用签名，T 的作用域在单个签名中。TypeScript 将在<strong>调用</strong> filter 类型的函数时为签名中的 T 绑定具体类型。每次调用 filter 都将会为 T 绑定独立的类型。</li><li>方式二：一个完整的调用签名，T 的作用域涵盖全部签名。由于 T 是 Filter 类型的一部分（而不属于某个具体的签名），因此 TypeScript 将在<strong>声明</strong> Filter 类型的函数时绑定 T。</li><li>方式三：与方式一类似，不过声明的不是完整的调用签名，而是简写形式。</li><li>方式四：与方式二类似，不过声明的不是完整的调用签名，而是简写形式。</li><li>方式五：一个具名函数调用签名，T 的作用域在签名中。TypeScript 将在<strong>调用</strong> filter 函数时为 T 绑定具体类型，每次调用 filter 都将会为 T 绑定独立的类型。</li></ol><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token generic-function"><span class="token function">map</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token punctuation">,</span> <span class="token constant">U</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>item<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">U</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">U</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span>
  <span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> array<span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    result<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">(</span>array<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">return</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述 map 函数需要两个泛型：输入数组中元素类型的 T,以及输出数组中元素类型的 U。这个函数接受的参数为一个 T 类型的数组和一个把 T 映射为 U 的函数。最后返回一个 U 类型的数组。</p><div class="custom-container tip"><p class="custom-container-title">标准库中的 filter 和 map 函数</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name"><span class="token builtin">Array</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token punctuation">{</span>
  <span class="token function">filter</span><span class="token punctuation">(</span>
    <span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">,</span> index<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">any</span><span class="token punctuation">,</span>
    thisArg<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span>
  <span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token generic-function"><span class="token function">map</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">U</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>
    <span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">,</span> index<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> array<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">U</span><span class="token punctuation">,</span>
    thisArg<span class="token operator">:</span> <span class="token builtin">any</span>
  <span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">U</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div></div><h3 id="_4-2-3-泛型推导" tabindex="-1"><a class="header-anchor" href="#_4-2-3-泛型推导" aria-hidden="true">#</a> 4.2.3 泛型推导</h3><p>在多数情况下，TypeScript 能自动推导出泛型。例如调用上一节的 map 函数 TypeSript 推导得出 T 的类型是 string，U 的类型是 boolean:</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">&#39;a&#39;</span><span class="token punctuation">,</span><span class="token string">&#39;b&#39;</span><span class="token punctuation">,</span><span class="token string">&#39;c&#39;</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">(</span>_<span class="token punctuation">)</span><span class="token operator">=&gt;</span>_<span class="token operator">===</span><span class="token string">&#39;a&#39;</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>也可以显示注解泛型。显示注解泛型时，要么所有必须的泛型都注解，要么都不注解。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token generic-function"><span class="token function">map</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token builtin">string</span><span class="token punctuation">,</span><span class="token builtin">boolean</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">&#39;a&#39;</span><span class="token punctuation">,</span><span class="token string">&#39;b&#39;</span><span class="token punctuation">,</span><span class="token string">&#39;c&#39;</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token punctuation">(</span>_<span class="token punctuation">)</span><span class="token operator">=&gt;</span>_<span class="token operator">===</span><span class="token string">&#39;a&#39;</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><p>TypeScript 将检查推导出来的每个泛型是否可赋值给显示绑定的泛型，入过不可赋值将会报错。</p><p>TypeScript 根据传入泛型函数的参数推导泛型的具体类型。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">let</span> p <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token builtin">number</span><span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

p<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> r <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_4-2-4-泛型别名" tabindex="-1"><a class="header-anchor" href="#_4-2-4-泛型别名" aria-hidden="true">#</a> 4.2.4 泛型别名</h3><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">MyEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">;</span>
  type<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> <span class="token class-name">ButtonEvent</span> <span class="token operator">=</span> MyEvent<span class="token operator">&lt;</span>HTMLButtonElement<span class="token operator">&gt;</span><span class="token punctuation">;</span>

<span class="token keyword">let</span> myEvent<span class="token operator">:</span> MyEvent<span class="token operator">&lt;</span>HTMLButtonElement <span class="token operator">|</span> <span class="token keyword">null</span><span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">&quot;#button&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  type<span class="token operator">:</span> <span class="token string">&quot;click&quot;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>在类型别名中只有这一个地方可以声明泛型，紧随类型别名的名称之后、赋值运算符（=）之前。</p><p>还可以使用 MyEvent 构建其他类型：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">TimedEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  event<span class="token operator">:</span> MyEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
  to<span class="token operator">:</span> Date<span class="token punctuation">;</span>
  from<span class="token operator">:</span> Date<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>泛型别名也可以在函数的签名中使用。TypeScript 为 T 绑定类型时，还会为 MyEvent 绑定：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code>
<span class="token keyword">function</span> <span class="token generic-function"><span class="token function">tirggerEvent</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>event<span class="token operator">:</span> MyEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token function">tirggerEvent</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  target<span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">&quot;#button&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  type<span class="token operator">:</span> <span class="token string">&quot;click&quot;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol><li>调用 triggerEvent 时传入一个对象</li><li>根据函数签名，TypeScript 认定传入参数类型必须为 MyEvent&lt;T&gt;。TypeScript 还未发现定义 MyEvent&lt;T&gt; 时声明的类型为 {target: T; type: string;}。</li><li>TypeScript 发现传给该对象 target 的值为 document.querySelector(&quot;#button&quot;)。意味着 T 的类型为 document.querySelector(&quot;#button&quot;) 的类型即：element | null。</li><li>TypeScript 检查全部代码，把 T 出现的位置替换为 element | null</li><li>TypeScript 确认所有类型都满足可赋值性，确保代码是类型安全的。</li></ol><h3 id="_4-2-5-受限的多态" tabindex="-1"><a class="header-anchor" href="#_4-2-5-受限的多态" aria-hidden="true">#</a> 4.2.5 受限的多态</h3><p>以二叉树为例：</p><ul><li>二叉树是一种数据结构</li><li>二叉树由节点构成</li><li>一个节点有一个值，最多可以指向两个子节点</li><li>节点由两种类型：叶节点（没有子节点）和内节点（至少有一个子节点）</li></ul><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">TreeNode</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> <span class="token class-name">LeafNode</span> <span class="token operator">=</span> TreeNode <span class="token operator">&amp;</span> <span class="token punctuation">{</span>
  isLife<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> <span class="token class-name">InnerNode</span> <span class="token operator">=</span> TreeNode <span class="token operator">&amp;</span> <span class="token punctuation">{</span>
  children<span class="token operator">:</span> <span class="token punctuation">[</span>TreeNode<span class="token punctuation">]</span> <span class="token operator">|</span> <span class="token punctuation">[</span>TreeNode<span class="token punctuation">,</span> TreeNode<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码：TreeNode 是一对象，只有一个属性是 value;LeafNode 类型具有 isLife 属性，值永远为 true,并且具有 TreeNode 的所有属性;InnerNode 类型有一个 children 属性，具有 children 属性，指向一个或两个节点，并且具备 TreeNode 的所有属性。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token generic-function"><span class="token function">mapNode</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> TreeNode<span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>node<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">,</span> <span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token operator">...</span>node<span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token function">f</span><span class="token punctuation">(</span>node<span class="token punctuation">.</span>value<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> a1 <span class="token operator">=</span> <span class="token function">mapNode</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> _<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> b1 <span class="token operator">=</span> <span class="token function">mapNode</span><span class="token punctuation">(</span>b<span class="token punctuation">,</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> _<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> c1 <span class="token operator">=</span> <span class="token function">mapNode</span><span class="token punctuation">(</span>c<span class="token punctuation">,</span> <span class="token punctuation">(</span>_<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> _<span class="token punctuation">.</span><span class="token function">toUpperCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>mapNode 函数定义了一个泛型 T。T 继承自 TreeNode,即 T 可以是 TreeNode，也可以是 TreeNode 的子类型。</li><li>mapNode 接受两个参数，第一个是类型为 T 的 node。前边指明了 node extends TreeNode，如果传入 TreeNode 之外类型，会提示错误。</li><li>mapNode 返回值类型为 T。</li></ul><p>如果只输入 T 没有继承 TreeNode,mapNode 会报出编译错误，因为不能从 T 类型的 node 中安全读取 node.value（T 的类型可以是 number等其他类型）；如果把 mapNode 声明为<code>(node:TreeNode,f:(value:string)=&gt;string）=&gt; TreeNode</code> 将会丢失信息，即 a1、b1、c1 的类型都会是 TreeNode 并不是传入时的类型。声明 <code>T extends TreeNode</code>，输入节点类型为（TreeNode、LeafNode 和 InnerNode）将得到保留，映射后类型也不变。</p><h4 id="有多个约束的受限多态" tabindex="-1"><a class="header-anchor" href="#有多个约束的受限多态" aria-hidden="true">#</a> 有多个约束的受限多态</h4><p>上述示例中，值为 T 施加了一个类型约束，即 T 至少为 TreeNode。也可以为 T 施加多个类型约束（扩展多个约束的交集）。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">HasSides</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  numberOfSides<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> <span class="token class-name">SidesHaveLength</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  sideLength<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token generic-function"><span class="token function">logPerimeter</span><span class="token generic class-name"><span class="token operator">&lt;</span>Shape <span class="token keyword">extends</span> HasSides <span class="token operator">&amp;</span> SidesHaveLength<span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>
  s<span class="token operator">:</span> Shape
<span class="token punctuation">)</span><span class="token operator">:</span> Shape <span class="token punctuation">{</span>
  <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span>numberOfSides<span class="token punctuation">,</span> s<span class="token punctuation">.</span>sideLength<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> s<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">type</span> <span class="token class-name">Square</span> <span class="token operator">=</span> HasSides <span class="token operator">&amp;</span> SidesHaveLength<span class="token punctuation">;</span>

<span class="token keyword">const</span> square<span class="token operator">:</span> Square <span class="token operator">=</span> <span class="token punctuation">{</span>
  numberOfSides<span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
  sideLength<span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token function">logPerimeter</span><span class="token punctuation">(</span>square<span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>logPerimeter 函数只接受一个参数，类型为 Shape</li><li>Shape 是一个泛型，同时扩展 HasSides 和 SidesHaveLength 类型。也就是说 Shape 至少有一定长度的边</li><li>logPerimeter 返回值与输入类型一样</li></ul><h4 id="使用首先得多态模拟变长参数" tabindex="-1"><a class="header-anchor" href="#使用首先得多态模拟变长参数" aria-hidden="true">#</a> 使用首先得多态模拟变长参数</h4><p>借助受限的多态还可以模拟变长参数函数（可以接受任意个参数的函数）</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">function</span> <span class="token generic-function"><span class="token function">call</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token builtin">unknown</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token constant">R</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span><span class="token function-variable function">f</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">R</span><span class="token punctuation">,</span> <span class="token operator">...</span>args<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">R</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">fill</span><span class="token punctuation">(</span>length<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token builtin">Array</span><span class="token punctuation">.</span><span class="token function">from</span><span class="token punctuation">(</span><span class="token punctuation">{</span> length <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">call</span><span class="token punctuation">(</span>fill<span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>call 是一个变长参数函数，有两个参数类型：T 和 R。T 是 unknow[] 的子类型，即 T 是任意类型的数组或元组。</li><li>call 的第一个参数是函数 f。f 也是变长参数函数，参数的类型与 args 一样，args 是什么类型，f 的参数就是什么类型。</li><li>除了函数 f 外，call 还接受数量不定的额外参数 ...args（剩余参数）。args 的类型是 T，T 必须是某一种数组类型。TypeScript 会根据具体传入的 args 推导 T 的类型。</li></ul><h3 id="_4-2-6-泛型默认类型" tabindex="-1"><a class="header-anchor" href="#_4-2-6-泛型默认类型" aria-hidden="true">#</a> 4.2.6 泛型默认类型</h3><p>泛型参数也可以指定默认类型。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 定义</span>
<span class="token keyword">type</span> <span class="token class-name">MyEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">;</span>
  type<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 使用</span>
<span class="token keyword">let</span> button<span class="token operator">:</span> MyEvent<span class="token operator">&lt;</span>HTMLButtonElement<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;#button&quot;</span><span class="token punctuation">)</span> <span class="token keyword">as</span> HTMLButtonElement<span class="token punctuation">,</span>
  type<span class="token operator">:</span> <span class="token string">&quot;123&quot;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 默认值</span>
<span class="token keyword">type</span> <span class="token class-name">MyEvent<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token operator">=</span> HTMLElement<span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">;</span>
  type<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token comment">// 为 T 设置限制确保 T 为 HTML 元素</span>
<span class="token keyword">type</span> <span class="token class-name">MyEvent<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> HTMLElement <span class="token operator">=</span> HTMLElement<span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">;</span>
  type<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 使用时无需在定义 T 的类型</span>
<span class="token keyword">let</span> myEvent<span class="token operator">:</span> MyEvent <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">&quot;button&quot;</span><span class="token punctuation">)</span> <span class="token keyword">as</span> HTMLElement<span class="token punctuation">,</span>
  type<span class="token operator">:</span> <span class="token string">&quot;click&quot;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>与函数的可选参数一样，有默认类型的泛型要放在没有默认类型的泛型后：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">MyEvent<span class="token operator">&lt;</span>Type <span class="token keyword">extends</span> <span class="token builtin">string</span><span class="token punctuation">,</span> Target <span class="token keyword">extends</span> HTMLElement <span class="token operator">=</span> HTMLElement<span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  target<span class="token operator">:</span> Target<span class="token punctuation">;</span>
  type<span class="token operator">:</span> Type<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_4-3-类型驱动开发" tabindex="-1"><a class="header-anchor" href="#_4-3-类型驱动开发" aria-hidden="true">#</a> 4.3 类型驱动开发</h2><p>类型驱动开发（type-driven development）</p><blockquote><p>类型驱动开发：先草拟类型签名，然后填充值的编程风格</p></blockquote><p>静态类型系统要求是约束表达式的值可以为类型。类型系统的表现力越强，提供的关于表达式中值的信息越多。使用表现力越强的系统类型注解函数。通过函数的类型签名就能知晓关于函数的多数信息。</p></div><!----><footer class="page-meta"><!----><!----><!----></footer><nav class="page-nav"><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%203%20%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E5%85%A8%E8%A7%A3.html" class="nav-link prev" aria-label="第 3 章 类型全解"><div class="hint"><span class="arrow left"></span>上一页</div><div class="link"><!---->第 3 章 类型全解</div></a><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html" class="nav-link next" aria-label="第 5 章 类和接口"><div class="hint">下一页<span class="arrow right"></span></div><div class="link">第 5 章 类和接口<!----></div></a></nav><!----><!----><!--]--></main><!--]--><!----><!--]--></div><!--]--><!----><!--]--></div>
    <script type="module" src="/doc/assets/app.41440b49.js" defer></script>
  </body>
</html>
